package xyz.archknight.app.loginput.dictionary.gen.ui

import com.hankcs.hanlp.HanLP
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.swing.Swing
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.deleteWhere
import org.jetbrains.exposed.sql.transactions.transaction
import xyz.archknight.app.loginput.dictionary.gen.bean.DatabaseProtocol
import xyz.archknight.app.loginput.dictionary.gen.bean.StatusProtocol
import xyz.archknight.app.loginput.dictionary.gen.dao.Word
import xyz.archknight.app.loginput.dictionary.gen.dao.Words
import xyz.archknight.app.loginput.dictionary.gen.service.EventBus
import xyz.archknight.app.loginput.dictionary.gen.ui.component.SVGButton
import xyz.archknight.app.loginput.dictionary.gen.ui.component.WordTable
import xyz.archknight.app.loginput.dictionary.gen.ui.model.WordTableModel
import java.awt.GridBagConstraints
import java.awt.GridBagLayout
import java.awt.Insets
import javax.swing.*
import javax.swing.event.TableModelEvent
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.min

/**
 * 主词库管理页面
 *
 * @author midnite
 * create at 2020-06-17 6:38 上午
 */
class MainStoragePane : JPanel() {
    private val constraints = GridBagConstraints()
    private val searchKeyword = JTextField()
    private val searchButton = SVGButton("magnifier", tip = "检索词库")
    private val resetSearchButton = SVGButton("back-circle", tip = "重置搜索")
    private val wordTableModel = WordTableModel()
    private val wordTable = WordTable(wordTableModel)
    private val scrollTable = JScrollPane(wordTable)
    private val firstButton = SVGButton("most-left", tip = "第一页")
    private val previousButton = SVGButton("left", tip = "前一页")
    private val nextButton = SVGButton("right", tip = "后一页")
    private val lastButton = SVGButton("most-right", tip = "最末页")
    private val toPage = JSpinner()
    private val removeItemButton = SVGButton("delete", tip = "删除所选记录")
    private val reloadButton = SVGButton("refresh-circle", tip = "刷新当前页面")
    private val commitButton = SVGButton("correct", tip = "保存当前改动")
    private val cancelButton = SVGButton("error", tip = "撤销所有未保存改动")

    private val pageSize: Int = 100
    private var currentPage: Int = 1
    private var totalPages: Int = 1

    init {
        EventBus.database.subscribe(this::databaseEventRoute)
        createUI()
    }

    private fun createUI() {
        layout = GridBagLayout()

        constraints.insets = Insets(4, 4, 4, 4)
        constraints.fill = GridBagConstraints.BOTH
        constraints.anchor = GridBagConstraints.NORTH
        addSubComponent(searchKeyword, weightX = 1.0, gridWidth = 13)
        searchKeyword.putClientProperty("JTextField.placeholderText", "输入关键词检索词库")
        addSubComponent(searchButton)
        addSubComponent(resetSearchButton, gridWidth = 0)

        addSubComponent(scrollTable, 1.0, 1.0, gridWidth = 0)
        wordTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
        createTableControl()

        wordTableModel.addTableModelListener(this::tableEventRoute)

        searchButton.addActionListener { loadData(currentPage) }
        resetSearchButton.addActionListener {
            searchKeyword.text = ""
            loadData(currentPage)
        }
        checkDirty()
    }

    private fun createTableControl() {
        addSubComponent(firstButton)
        addSubComponent(previousButton)
        val separatorLeft = JPanel()
        addSubComponent(separatorLeft, 1.0, gridWidth = 2)
        addSubComponent(reloadButton)
        addSubComponent(removeItemButton)
        addSubComponent(commitButton)
        addSubComponent(cancelButton)
        val separatorMid = JPanel()
        addSubComponent(separatorMid, 1.0, gridWidth = 2)
        addSubComponent(toPage)
        val separatorRight = JPanel()
        addSubComponent(separatorRight, 1.0, gridWidth = 2)
        addSubComponent(nextButton)
        addSubComponent(lastButton)

        firstButton.addActionListener { loadData(1) }
        previousButton.addActionListener { loadData(currentPage - 1) }
        toPage.addChangeListener { loadData(toPage.value as Int) }
        nextButton.addActionListener { loadData(currentPage + 1) }
        lastButton.addActionListener { loadData(totalPages) }
        reloadButton.addActionListener { loadData(currentPage) }
        commitButton.addActionListener { commitChanges() }
        cancelButton.addActionListener {
            if (wordTableModel.hasDirty()) {
                loadData(currentPage)
            }
        }

        removeItemButton.addActionListener { removeWord() }
    }

    private fun addSubComponent(
        component: JComponent, weightX: Double = 0.0, weightY: Double = 0.0,
        gridWidth: Int = 1, gridHeight: Int = 1
    ) {
        constraints.weightx = weightX
        constraints.weighty = weightY
        constraints.gridwidth = gridWidth
        constraints.gridheight = gridHeight
        add(component, constraints)
    }

    private fun databaseEventRoute(action: DatabaseProtocol) {
        when (action) {
            DatabaseProtocol.RefreshMain, DatabaseProtocol.RefreshBoth -> {
                loadData(1)
            }
            DatabaseProtocol.UpdateMain -> {
                loadData(currentPage)
            }
            else -> {
            }
        }
    }

    private fun tableEventRoute(action: TableModelEvent) {
        when (action.type) {
            TableModelEvent.UPDATE -> {
                if (action.column == 0) {
                    val word = wordTableModel.getValueAt(action.firstRow, action.column) as String
                    val pinyin = HanLP.convertToPinyinString(word, "'", false)
                    wordTableModel.update(action.firstRow, word, pinyin)
                }
                if (action.column == 1){
                    wordTableModel.markDirty(action.firstRow)
                }
                checkDirty()
            }
            else -> {
            }
        }
    }

    private fun checkDirty() {
        commitButton.isEnabled = wordTableModel.hasDirty()
        cancelButton.isEnabled = wordTableModel.hasDirty()
    }

    private fun loadData(page: Int = 1) {
        if (wordTableModel.hasDirty()) {
            val confirm = JOptionPane.showConfirmDialog(
                this@MainStoragePane,
                "当前页面已经有内容编辑过了，继续操作将导致这些内容丢失，是否确定？",
                "数据丢失提示",
                JOptionPane.YES_NO_OPTION,
                JOptionPane.QUESTION_MESSAGE
            )
            if (confirm == JOptionPane.NO_OPTION) {
                return
            }
        }
        GlobalScope.launch(Dispatchers.IO) {
            transaction {
                val wordsCount = Word.count()
                EventBus.refreshStatus(StatusProtocol.Main, wordsCount)
                totalPages = ceil(wordsCount / pageSize.toDouble()).toInt()
                currentPage = min(totalPages, max(page, 1))
                launch(Dispatchers.Swing) {
                    if (totalPages != 0) {
                        toPage.model = SpinnerNumberModel(currentPage, 1, totalPages, 1)
                    } else {
                        toPage.model = SpinnerNumberModel(currentPage, 0, 0, 1)
                    }
                }
                var originalList: List<Word>
                if (wordsCount > 0) {
                    do {
                        originalList = if (searchKeyword.text.isEmpty())
                            Word.all()
                                .limit(pageSize, offset = pageSize * (currentPage - 1).toLong())
                                .toList()
                        else
                            Word.find { Words.phase like "%${searchKeyword.text}%" }
                                .limit(pageSize, offset = pageSize * (currentPage - 1).toLong())
                                .toList()
                        if (originalList.isEmpty()) {
                            currentPage--
                        }
                    } while (originalList.isEmpty())
                } else {
                    originalList = emptyList()
                }
                wordTableModel.reset()
                originalList.forEach { wordTableModel.load(it.id.value, it.phase, it.pinyin) }
                checkDirty()
            }
        }
    }

    private fun removeWord() {
        if (wordTable.selectedRowCount > 0) {
            GlobalScope.launch(Dispatchers.Swing) {
                wordTable.selectedRows
                    .filter { !wordTableModel.isNewLine(it) }
                    .forEach {
                        wordTableModel.remove(it)
                    }
            }
        }
        checkDirty()
    }

    private fun commitChanges() {
        GlobalScope.launch(Dispatchers.IO) {
            val newPhases = wordTableModel.filter { it.dirty }.map { it.phase }
            val newPinyins = wordTableModel.filter { it.dirty }.map { it.pinyin }
            transaction {
                if (Word.find {
                        (Words.phase inList newPhases) and
                            (Words.pinyin inList newPinyins) }.count() > 0) {
                    launch(Dispatchers.Swing) {
                        JOptionPane.showMessageDialog(
                            this@MainStoragePane,
                            "待保存内容在数据库中已经存在，请检查。",
                            "词条已存在",
                            JOptionPane.ERROR_MESSAGE
                        )
                    }
                } else {
                    wordTableModel.filter { it.removed }
                        .forEach { rec ->
                            transaction {
                                Words.deleteWhere { Words.id eq rec.id }
                            }
                        }
                    wordTableModel.filter { it.dirty }
                        .forEach { rec ->
                            println(rec)
                            transaction {
                                if (rec.new) {
                                    Word.new {
                                        phase = rec.phase
                                        pinyin = rec.pinyin
                                    }
                                } else {
                                    Word.findById(rec.id)?.let { word ->
                                        println("${word.phase}, ${rec.phase}, eq: ${word.phase ==
                                         rec.phase}")
                                        if (word.phase != rec.phase) {
                                            word.phase = rec.phase
                                        }
                                        word.pinyin = rec.pinyin
                                    }
                                }
                            }
                        }
                    wordTableModel.markAllClean()
                }
            }
            loadData(currentPage)
        }
    }
}